from django.shortcuts import render
from asgiref.sync import sync_to_async
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
import random

# Create your views here.
async def index(request):
    return await sync_to_async(render)(request, 'aStar.html')

@csrf_exempt
def start_path_finding(request):
    data = json.loads(request.body)
    start = data['startPoint']
    end = data['endPoint']
    maze = data['maze']
    res = a_star_algorithm(start, end, maze)
    return JsonResponse({'res': res})

@csrf_exempt
def randomize_map(request):
    try:
        n, m = 50, 50
        maze = [[random.choice([0, 1]) for _ in range(m)] for _ in range(n)]
        return JsonResponse({'maze': maze})
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)

# 全局变量来存储当前的启发函数类型，默认为曼哈顿距离
current_heuristic_type = 'manhattan'

def heuristic(a, b):
    if current_heuristic_type == 'manhattan':
        return abs(a.x - b.x) + abs(a.y - b.y)
    elif current_heuristic_type == 'euclidean':
        return ((a.x - b.x) ** 2 + (a.y - b.y) ** 2) ** 0.5
    elif current_heuristic_type == 'chebyshev':
        return max(abs(a.x - b.x), abs(a.y - b.y))
    else:
        return abs(a.x - b.x) + abs(a.y - b.y)

@csrf_exempt
def set_heuristic(request):
    global current_heuristic_type
    heuristic_type = request.GET.get('type', 'manhattan')  # 获取启发函数类型
    current_heuristic_type = heuristic_type
    return JsonResponse({'heuristic': heuristic_type})


import heapq

class Node:
    def __init__(self, x, y, cost=0, parent=None):
        self.x = x
        self.y = y
        self.cost = cost
        self.parent = parent
        self.g = 0  # Cost from start to node
        self.h = 0  # Heuristic cost from node to end
        self.f = 0  # Total cost

    def __lt__(self, other):
        return self.f < other.f

def get_neighbors(node, maze):
    directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]  # Down, Right, Up, Left
    neighbors = []
    for dx, dy in directions:
        nx, ny = node.x + dx, node.y + dy
        if 0 <= nx < len(maze) and 0 <= ny < len(maze[0]):
            if maze[ny][nx] == 0:  # 注意这里的索引顺序
                neighbors.append(Node(nx, ny))
    return neighbors

def a_star_algorithm(start, end, maze):
    start_node = Node(start['x'], start['y'])
    end_node = Node(end['x'], end['y'])
    open_set = []
    open_set_hash = set()
    heapq.heappush(open_set, start_node)
    open_set_hash.add((start_node.x, start_node.y))
    closed_set = set()
    expanded_nodes = 0
    count = 0
    generated_nodes = 1  # 包括起始节点

    while open_set:
        current_node = heapq.heappop(open_set)
        open_set_hash.remove((current_node.x, current_node.y))
        closed_set.add((current_node.x, current_node.y))
        expanded_nodes += 1

        if current_node.x == end_node.x and current_node.y == end_node.y:
            path = []
            while current_node:
                count += 1
                path.append({'x': current_node.x, 'y': current_node.y})
                current_node = current_node.parent
            path_length = len(path)
            return {
                'path': path[::-1],
                'path_length': path_length,
                'expanded_nodes': expanded_nodes,
                'generated_nodes': generated_nodes,
                'count': count
            }

        neighbors = get_neighbors(current_node, maze)
        for neighbor in neighbors:
            count += 1
            if (neighbor.x, neighbor.y) in closed_set:
                continue
            generated_nodes += 1
            tentative_g = current_node.g + 1
            if (neighbor.x, neighbor.y) in open_set_hash and tentative_g >= neighbor.g:
                continue
            neighbor.g = tentative_g
            neighbor.h = heuristic(neighbor, end_node)
            neighbor.f = neighbor.g + neighbor.h
            neighbor.parent = current_node
            if (neighbor.x, neighbor.y) not in open_set_hash:
                heapq.heappush(open_set, neighbor)
                open_set_hash.add((neighbor.x, neighbor.y))

    return {
        'path': [],
        'path_length': 0,
        'expanded_nodes': expanded_nodes,
        'generated_nodes': generated_nodes,
        'count': 0
    }




